

#include <stdio.h>
#include <string.h>
#include "glib_example.h"
#include "xm_log.h"
#include "http_parser.h"

#include <glib.h>
#include <gio/gio.h>


typedef struct xhttp_client * xhttp_client_t;

typedef struct xhttp_client{
    char * url;
    char * host;
    char * ip;
    char * path;
    struct http_parser_url u;
    GSocketClient * socket_client;
    GResolver * resolver;
    GCancellable * cancellable;
    gboolean free_req;
    gboolean in_progress;
}xhttp_client;

static inline
void xhttp_cleanup_url(xhttp_client * obj){
    if(obj->url){
        g_free(obj->url);
        obj->url = NULL;
    }
    
    if(obj->host){
        g_free(obj->host);
        obj->host = NULL;
    }
    
    if(obj->ip){
        g_free(obj->ip);
        obj->ip = NULL;
    }
    
    if(obj->path){
        g_free(obj->path);
        obj->path = NULL;
    }
    
    if(obj->resolver){
        // never reach hear ?
        if(obj->resolver){
            g_object_unref(obj->resolver);
            obj->resolver = NULL;
        }
    }
}

static inline
gboolean xhttp_is_in_progress(xhttp_client * obj){
    return (obj->resolver != NULL) || obj->in_progress ;
}

void xhttp_client_uninit(xhttp_client * obj){
    if(!obj) return;
    
    obj->free_req = TRUE;
    if(xhttp_is_in_progress(obj)){
        gdbgi("free but in-progress");
        g_cancellable_cancel(obj->cancellable);
        return;
    }
    
    xhttp_cleanup_url(obj);
    
    g_object_unref(obj->cancellable);
    memset(obj, 0, sizeof(xhttp_client));
}

void xhttp_client_init(xhttp_client * obj){
    memset(obj, 0, sizeof(xhttp_client));
    obj->cancellable = g_cancellable_new ();
}









static
const char * UFIELD_NAMES[] = {
    "UF_SCHEMA",
    "UF_HOST",
    "UF_PORT",
    "UF_PATH",
    "UF_QUERY",
    "UF_FRAGMENT",
    "UF_USERINFO",
    "UF_MAX"
};

static inline
void dump_url(const char *url, struct http_parser_url *u)
{
    char part[512];
    int i;
    
    gdbgi("url=[%s]", url);
    gdbgi("\tfields: 0x%x, port: %u\n", u->field_set, u->port);
    for (i = 0; i < UF_MAX; i++) {
        if (u->field_set & (1 << i)) {
            memcpy(part, url + u->field_data[i].off,
                   u->field_data[i].len);
            part[u->field_data[i].len] = '\0';
        } else
            part[0] = '\0';
        
        gdbgi("\tfield[%s]: off: %u len: %u part: \"%s\"\n", UFIELD_NAMES[i],
              u->field_data[i].off, u->field_data[i].len, part);
    }
}




static inline
gboolean check_address_ipv4(const char * address){
    if (strchr (address, ':')){
        return FALSE; // ipv6
    }
    return TRUE;
}

static inline
gchar * select_ip_from_resolved_addresses(GList *addresses, gboolean is_free){
    gchar * addr = NULL;
    char *phys;
    GList *a;
    for (a = addresses; a; a = a->next){
        phys = g_inet_address_to_string ((GInetAddress *)a->data);
        //        gdbgi ("Address: [%s] [is_ip=%d]", phys, g_hostname_is_ip_address(phys));
        if(!addr){
            addr = g_strdup(phys);
        }else{
            if(check_address_ipv4(phys)){
                g_free(addr);
                addr = g_strdup(phys);
                break;
            }
        }
        if(is_free){
            g_free (phys);
            g_object_unref (a->data);
        }
    }
    if(is_free){
        g_list_free (addresses);
    }
    return addr;
}


static void
xhttp_lookup_by_name_callback (GObject *source, GAsyncResult *result, gpointer user_data){
    xhttp_client * obj = (xhttp_client *) user_data;
    GResolver * resolver = obj->resolver;
    GError *error = NULL;
    GList *addresses;
    
    do{
        addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
        if (error){
            gdbgi ("resolve error: [%s]", error->message);
            g_error_free (error);
            break ;
        }
        
        obj->ip = select_ip_from_resolved_addresses(addresses, 1);
        gdbgi("resolved host [%s] -> [%s]", obj->host, obj->ip);
        
        if(obj->free_req){
            break;
        }
        
    }while(0);
    
    if(obj->resolver){
        // never reach hear ?
        if(obj->resolver){
            g_object_unref(obj->resolver);
            obj->resolver = NULL;
        }
    }
    
    if(obj->free_req && !xhttp_is_in_progress(obj)){
        gdbgi("free in resolved");
        xhttp_client_uninit(obj);
    }
}

int xhttp_client_set_url(xhttp_client * obj, const char * url){
    int ret = - 1;
    do{
        if(xhttp_is_in_progress(obj)){
            gdbge("post but in-progress");
            ret = -1;
            break;
        }
        
        xhttp_cleanup_url(obj);
        
        obj->url = g_strdup(url);
        
        const int is_connect = 0;
        ret = http_parser_parse_url(url, strlen(url), is_connect, &obj->u);
        if(ret){
            break;
        }
        if( !(obj->u.field_set & (1 << UF_HOST))) {
            ret = -1;
            break;
        }
        
        if(!(obj->u.port > 0)){
            obj->u.port = 80;
        }
        
        obj->host = g_strndup(((obj)->url+(obj)->u.field_data[UF_HOST].off), (obj)->u.field_data[UF_HOST].len);
        if( !(obj->u.field_set & (1 << UF_PATH))) {
            obj->path = g_strdup("/");
            obj->u.field_set |= (1 << UF_PATH);
            (obj)->u.field_data[UF_PATH].len = 1;
        }else{
            obj->path = g_strndup(((obj)->url+(obj)->u.field_data[UF_PATH].off), (obj)->u.field_data[UF_PATH].len);
        }
        
        
        if(g_hostname_is_ip_address(obj->host)){
            gdbgi("address is ip, [%s]", obj->host);
            obj->ip = g_strdup(obj->host);
        }
        
        ret = 0;
        
    }while(0);
    return ret;
}

static
void socket_client_connect_callback (GObject *source_object,
                                     GAsyncResult *result,
                                     gpointer user_data){
    
    xhttp_client * obj = (xhttp_client *) user_data;
    GError *error = NULL;
    GSocketConnection *socket_conn;
    
    do{
        socket_conn = g_socket_client_connect_finish (obj->socket_client, result, &error);
        if (error){
            gdbgi ("connect error: [%s] [%s]", obj->host, error->message);
            g_error_free (error);
            break ;
        }
        
        gdbgi("connectd to host [%s]", obj->host);
        
        if(obj->free_req){
            obj->in_progress = FALSE;
            break;
        }
        
    }while(0);
    
    if(obj->free_req && !xhttp_is_in_progress(obj)){
        gdbgi("free in resolved");
        xhttp_client_uninit(obj);
    }
    
}

int xhttp_client_post(xhttp_client * obj, const char * url){
    int ret = - 1;
    do{
        if(xhttp_is_in_progress(obj)){
            gdbge("post but in-progress");
            ret = -1;
            break;
        }
        
        if(url){
            ret = xhttp_client_set_url(obj, url);
            if(ret){
                break;
            }
        }else if(!obj->url){
            gdbge("post but NOT set url");
            ret = -1;
            break;
        }
        
        const char * host = obj->ip ? obj->ip : obj->host;
        
        //        if(!obj->ip){
        //
        //            gdbgi("resolving host [%s] ...", host);
        //            obj->resolver = g_resolver_get_default ();
        //            g_resolver_lookup_by_name_async(obj->resolver
        //                                            , host
        //                                            , obj->cancellable
        //                                            , xhttp_lookup_by_name_callback
        //                                            , (gpointer)obj);
        //        }
        
        if(!obj->socket_client){
            obj->socket_client = g_socket_client_new ();
        }
        obj->in_progress = TRUE;
        gdbgi("connecting to host [%s] ...", host);
        g_socket_client_connect_to_host_async (obj->socket_client,
                                               host,
                                               obj->u.port,
                                               obj->cancellable,
                                               socket_client_connect_callback,
                                               obj);
        
        ret = 0;
        
    }while(0);
    return ret;
}

static void
lookup_by_name_callback (GObject *source, GAsyncResult *result, gpointer user_data){
    GResolver * resolver = (GResolver *)user_data;
    GError *error = NULL;
    GList *addresses;
    
    gdbgi("resolve done");
    addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
    if (error){
        gdbgi ("Error: [%s]", error->message);
        g_error_free (error);
        return ;
    }
    char * ip = select_ip_from_resolved_addresses(addresses, 1);
    gdbgi("select ip: [%s]", ip);
    g_free(ip);
    //    print_resolved_addresses (addresses);
}

void test_parse_url(){
    const char * urls[] = {
        "http://localhost",
        "http://localhost/",
        "http://localhost/aaaa",
        "http://localhost:9910/",
        "http://-:9910/",
        "http://-/",
        NULL
    };
    
    struct http_parser_url u;
    
    for(int i = 0; urls[i]; i++){
        const char * url = urls[i];
        int is_connect = 0;
        http_parser_parse_url(url, strlen(url), is_connect, &u);
        dump_url(url, &u);
    }
}


void test_xhttp_client(){
    //    test_parse_url();
    xhttp_client * httpclt = (xhttp_client *) g_slice_alloc0(sizeof(xhttp_client));
    xhttp_client_init(httpclt);
    xhttp_client_set_url(httpclt, "http://dddddd/aaaa");
    xhttp_client_post(httpclt, NULL);
    //    xhttp_client_uninit(httpclt);
}












#include <libsoup/soup.h>

static inline
void dump_soup_msg(SoupMessage *msg){
    gdbgi("msg->status_code=%d", msg->status_code);
    gdbgi("msg->reason_phrase=%s", msg->reason_phrase);
    gdbgi("msg->response_body=%p", msg->response_body);
    if(msg->response_body){
        gdbgi("msg->response_body->data=%p", msg->response_body->data);
        gdbgi("msg->response_body->length=%d", msg->response_body->length);
    }
}


void test_libsoup_sync(const char * url){
    SoupSession * session = soup_session_sync_new ();
    SoupMessage *msg = soup_message_new ("GET", url);
    GInputStream *stream;
    GError *error = NULL;
    stream = soup_session_send (session, msg, (GCancellable *)NULL, &error);
    dump_soup_msg(msg);
    uint8_t buffer[1024];
    while(1){
        error = NULL;
        gssize sz = g_input_stream_read (stream, buffer, 1024, (GCancellable *)NULL, &error);
        fwrite (buffer, 1, sz, stdout);
    }
}


static
void my_callback (GObject *object, GAsyncResult *result, gpointer user_data)
{
    GInputStream *stream;
    GError *error = NULL;
    
    stream = soup_session_send_finish (SOUP_SESSION (object), result, &error);
    SoupMessage * msg = (SoupMessage *) user_data;
    dump_soup_msg(msg);
    
}

void test_libsoup_async(const char * url){
    SoupSession * session = soup_session_sync_new ();
    SoupMessage *msg = soup_message_new ("GET", url);
    soup_session_send_async (session, msg, (GCancellable *)NULL, my_callback, msg);
}


static
void soup_msg_callback (SoupSession *session,
                        SoupMessage *msg,
                        gpointer user_data){
    dump_soup_msg(msg);
    fwrite (msg->response_body->data,
            1,
            msg->response_body->length,
            stdout);
}

void test_libsoup_queue_get(const char * url){
    SoupSession * session = soup_session_sync_new ();
    SoupMessage *msg = soup_message_new ("GET", url);
    soup_session_queue_message (session, msg, soup_msg_callback, msg);
    g_object_ref (msg);
    gdbgi("soup message queued");
}

void test_libsoup_queue_post(const char * url){
    const char * json = "{}";
    int json_length = strlen(json);
    
    SoupSession * session = soup_session_sync_new ();
    SoupMessage *msg = soup_message_new ("POST", url);
    soup_message_set_request (msg, "application/json", SOUP_MEMORY_TEMPORARY,
                              json, json_length);
    
    soup_session_queue_message (session, msg, soup_msg_callback, msg);
    g_object_ref (msg);
    gdbgi("soup message queued");
}

void test_libsoup(){
    const char * url = "http://localhost:1880/";
//    test_libsoup_sync(url);
//    test_libsoup_async(url);
//    test_libsoup_queue_get(url);
    test_libsoup_queue_post(url);
    
    
    //    SoupSession *session = soup_session_sync_new();
    //    SoupMessage *msg = soup_message_new ("GET", "http://www.baidu.com/");
    //    soup_session_send_message (session, msg);
    //    dump_soup_msg(msg);
    //    fwrite (msg->response_body->data,
    //            1,
    //            msg->response_body->length,
    //            stdout);
    
    
    
}








void test_glib_example(){
    test_parse_url();
//    test_libsoup();
//    test_xhttp_str();
}

